package by.bsu.scs_bsu.file.tools;

import java.io.File;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;

import by.bsu.scs_bsu.resources.managers.DataStorageManager;
import by.bsu.scs_bsu.resources.managers.MessageManager;

public class FileUploader {
	
	private static final Logger LOGGER = Logger.getLogger("system_logger");
	
	// We use to multiply value defined in property file to get value in bytes.
	private static final int BYTES_IN_MB = 1024 * 1024;
	
	private HttpServletRequest request;
	private String fileName;
	private String errorReason;
	private boolean uploadSuccessStatus;
	
	public FileUploader(HttpServletRequest request) {
		this.request = request;
		fileName = "";
		errorReason = "";
		uploadSuccessStatus = false;
	}
	
	@SuppressWarnings("rawtypes")
	public boolean uploadFile() {
		boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
	        return uploadSuccessStatus;
        }
	    DiskFileItemFactory factory = new DiskFileItemFactory();
	    // Максимальный буфера данных в байтах,
	    // при его привышении данные начнут записываться на диск во временную директорию
	    int tempFileSize = getSizeInBytes(DataStorageManager.TEMP_DIR_SIZE);
	    factory.setSizeThreshold(tempFileSize);
	    // устанавливаем временную директорию
	    File tempDir = (File)request.getServletContext().getAttribute("javax.servlet.context.tempdir");
	    factory.setRepository(tempDir);	
	    ServletFileUpload upload = new ServletFileUpload(factory);
	    //максимальный размер данных который разрешено загружать в байтах
	    int maxFileSize = getSizeInBytes( DataStorageManager.MAX_FILE_SIZE );
      	upload.setSizeMax(maxFileSize);
      	try {
			List items = upload.parseRequest(request);
	         Iterator iter = items.iterator();
	         while (iter.hasNext()) {
	        	 FileItem item = (FileItem) iter.next();
	        	 if (!item.isFormField()) {
	        		// check file size < max allowed
	        		if( isFileSizeValid(item) && isFileTypeValid(item) ) {
	        			processUploadedFile(item);
	        			uploadSuccessStatus = true;
	        		} else {
	        			uploadSuccessStatus = false;
	        		}	
	        	 }
             }       
	     } catch (Exception e) {
	    	 LOGGER.error(e.toString());
	    	 uploadSuccessStatus = false;
	     }  
	     return uploadSuccessStatus;
	}
	
	private void processUploadedFile(FileItem item) throws Exception {
	      File uploadetFile = null;
	      fileName = getUniqueFileName( item.getName() ); 
	      String uploadDir = DataStorageManager.getProperty(DataStorageManager.UPLOAD_DIR);
	      String path = request.getServletContext().getRealPath(uploadDir + fileName);               
	      uploadetFile = new File(path);      
	      uploadetFile.createNewFile();
	      item.write(uploadetFile);
	}
	
	private int getSizeInBytes( String propertyName ) {
		int sizeInMb = Integer.parseInt( DataStorageManager.getProperty( propertyName ) );
		return BYTES_IN_MB * sizeInMb;
	}
	
	private String getUniqueFileName( String name ) {
		String uniquePrefix = Long.toString( ( new Date() ).getTime() );
		return String.format( "%s_%s", uniquePrefix, name );
	}
	
	private boolean isFileSizeValid(FileItem file) {
		boolean isValid;
		int maxSize = getSizeInBytes( DataStorageManager.MAX_FILE_SIZE );
		if( file.getSize() >  maxSize ) {
			isValid = false;
			errorReason = MessageManager.getProperty( MessageManager.FILE_SIZE_ERROR );
		} else {
			isValid = true;
		}
		return isValid;
	}
	
	private boolean isFileTypeValid( FileItem file ) {
		boolean isValid = false;
		String legalTypes = DataStorageManager.getProperty( DataStorageManager.VALID_FILE_TYPES );
		StringTokenizer sT = new StringTokenizer(legalTypes, ";");
		while( sT.hasMoreTokens() ) {
			String namePattern = "(\\w+)" + sT.nextToken(); 
			Pattern p = Pattern.compile( namePattern );
			Matcher m = p.matcher( file.getName() );
			if( m.matches() ) {
				isValid = true;
				break;
			}
		}
		if( !isValid ) {
			errorReason = MessageManager.getProperty( MessageManager.FILE_TYPE_ERROR );
		}
		return isValid;
	}
	
	public String getErrorReason() {
		return errorReason;
	}
}

